﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using FileSystemControls;
using System.Threading;
using System.Threading.Tasks;
using FileSystemControls.ListViewItems;
using FileSystemControls.Support;
using Timer = System.Threading.Timer;

namespace FileTracker.Forms
{
    //TODO: Do the copy work in this form. 
    //We are putting the rules in a central location so, Copy will be part of the rules and the item will know nothing except how to load itself.
    public partial class FormCopyProgress : Form
    {

        #region ** Member Variables **

        readonly List<FileSystemListViewItem> _selectedItems;
        readonly string _destinationPath;
        FileSystemListViewItem _currentItem;

        private string _labelCountString = "{0} of {1} bytes in {2} files";
        private object _itemCountLock;
        private int _itemCount;
        private long _byteCount;

        private BackgroundWorker compileWorker;
        private Timer _itemCountTimer;

        #endregion

        public FormCopyProgress(List<FileSystemListViewItem> argSelectedItems, string argDestPath)
        {
            InitializeComponent();

            _itemCountLock = new object();

            _selectedItems = new List<FileSystemListViewItem>();
            progressBarExCopyProgressFiles.Value = 0;
            progressBarExCopyProgressBytes.Value = 0;

            _selectedItems = argSelectedItems;
            _destinationPath = argDestPath;

            if (!ValidateProperties())
            {
                Close();
            }
        }

        private void FormCopyProgress_Shown(object sender, EventArgs argEventArgs)
        {
            _itemCountTimer = new Timer(CountProgressCallback, null, 500, 500);
            compileWorker = new BackgroundWorker();

            compileWorker.WorkerSupportsCancellation = true;
            compileWorker.WorkerReportsProgress = false; //Progress will be reported by a timer thread.
            compileWorker.DoWork += compileWorker_DoWork;
            compileWorker.RunWorkerCompleted += compileWorker_RunWorkerCompleted;

            labelCurrentFileName.Text = "Please wait, computing copy size";


            compileWorker.RunWorkerAsync();
        }

        private void CountProgressCallback(object state)
        {
            Action updateAction = UpdateCountString;
            labelBytesRemaining.Invoke(updateAction);
        }

        private void UpdateCountString()
        {
            labelBytesRemaining.Text = String.Format(_labelCountString, "0", "0", _itemCount);
        }

        void compileWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            CountFiles(_selectedItems);
        }

        void compileWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _itemCountTimer.Change(Timeout.Infinite, Timeout.Infinite);
            _itemCountTimer.Dispose();

            var copyQuestion = String.Format("Ready to copy {0} items '{1}'. Continue?", _selectedItems.Count, _destinationPath);

            var continueCopy = MessageBox.Show(this, copyQuestion, "Continue copying?", MessageBoxButtons.YesNo);
            if (continueCopy == DialogResult.No)
            {
                Close();
                return;
            }

            labelCopy.Visible = true;

            CopyItems();
        }

        private bool ValidateProperties()
        {
            if (_selectedItems == null)
            {
                throw new ArgumentException("SelectedItems is null.");
            }

            if (_selectedItems.Count <= 0)
            {
                //throw new ArgumentException("SelectedItems has a count <= 0.");
                return false;
            }

            if (String.IsNullOrEmpty(_destinationPath))
            {
                throw new ArgumentException("DestinationPath is null or empty.");
            }

            if (!Directory.Exists(_destinationPath))
            {
                throw new ArgumentException("DestinationPath does not exist.");
            }
            return true;
        }

        private void CountFiles(List<FileSystemListViewItem> selectedItems)
        {
            foreach (FileSystemListViewItem currentItem in selectedItems)
            {
                //Count the files and byte sizes. 
                if (currentItem is FileListViewItem)
                {
                    _itemCount++;
                    _byteCount += currentItem.Length;
                }
                else
                {
                    //If any of them are directories step into them
                    DirectoryListViewItem tempItem = currentItem as DirectoryListViewItem;
                    if (tempItem != null)
                    {
                        CountFiles(tempItem.GetSubItems());
                    }
                }
    
            }

        }

        //TODO: 006 - Make sure this can handle modifying the UI thread
        protected void CopyItems()
        {
            {

                //Don't loop because now the work is done on a seperate thread.
                //_currentItem_CopyComplete() will remove the item and recurse this method again
                //Check if there are other items to process
                if (_selectedItems.Count > 0)
                {
                    //Doesn't matter which file is copied. [0] is garaunteed to be there
                    _currentItem = _selectedItems[0];

                    //SetCurrentFilename localInvoke = new SetCurrentFilename(LocalSetFilename);
                    Action<string> localInvoke = new Action<string>(LocalSetFilename);

                    labelCurrentFileName.Invoke(localInvoke, _currentItem.FullPath);

                    _currentItem.CopyProgress += new EventHandler<CopyProgressEventArgs>(UpdateProgress);
                    _currentItem.CopyComplete += new EventHandler(_currentItem_CopyComplete);

                    progressBarExCopyProgressBytes.Maximum = _currentItem.Length;

                    try
                    {
                        //_log.Debug("Copy the item");
                        //_currentItem.CopyTo(_destinationPath);
                    }
                    catch (FileExistsException ex)
                    {
                        DialogResult FileExistsResult;
                        FileExistsResult = MessageBox.Show(_currentItem.Name + " already exists in " + _destinationPath + ".\r\n Are you sure you want to overwrite it?", "File exists", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                        if (FileExistsResult == DialogResult.Yes)
                        {
                            _currentItem.CopyTo(_destinationPath, true);
                        }

                    }

                    Console.WriteLine(@"CopyItem(Back from Item) Thread: " + Thread.CurrentThread.ManagedThreadId.ToString());

                }
                else
                {
                    //CloseInvoke localInvoke = new CloseInvoke(this.Close);
                    Action localInvoke = new Action(this.Close);
                    this.Invoke(localInvoke);
                }
            }
        }

        private void LocalSetFilename(string argFilename)
        {
            labelCurrentFileName.Text = argFilename;
        }

        //TODO: 008 - Isolate the label update and provide some level of UIThread isolation.
        public void UpdateProgress(object sender, CopyProgressEventArgs ProgressValue)
        {
            {
                Action<string> SetTextMethod;
                string ProgressText;

                progressBarExCopyProgressBytes.Value = ProgressValue.CopyPosition;

                ProgressText = (progressBarExCopyProgressBytes.Value / 1024).ToString() + "k of " + progressBarExCopyProgressBytes.Maximum.ToString();

                SetTextMethod = SetProgressText;
                labelBytesRemaining.Invoke(SetTextMethod, ProgressText);

                return;
            }
        }

        private void SetProgressText(string argProgressText)
        {
            labelBytesRemaining.Text = argProgressText;
        }

        void _currentItem_CopyComplete(object sender, EventArgs argEventArgs)
        {
            {

                //Advance the Files progress bar by one
                progressBarExCopyProgressFiles.PerformStep();

                //Set the Bytes progress bar to empty
                progressBarExCopyProgressBytes.Value = 0;

                //Remove the CurrentItem from the collection
                _selectedItems.Remove(_currentItem);

                //Call CopyItems to copy the remaining items (if any)
                CopyItems();
            }
        }

        private void buttonCancel_Click(object sender, EventArgs argEventArgs)
        {
            _currentItem.CopyCancel();
        }

        //public EventHandler<CopyProgressEventArgs> CurrentItem_CopyProgress { get; set; }
    }

}
